package com.xci.platform.auth.service;

import com.github.pagehelper.Page;
import com.xci.platform.annotation.OperateUser;
import com.xci.platform.annotation.QueryMap;
import com.xci.platform.auth.core.AuthConstant;
import com.xci.platform.auth.core.AuthDataSetting;
import com.xci.platform.auth.core.HistoryModel;
import com.xci.platform.auth.core.HistoryOperateType;
import com.xci.platform.auth.dao.ObjectDataMapDao;
import com.xci.platform.auth.dao.ObjectMapDao;
import com.xci.platform.auth.dao.RoleDao;
import com.xci.platform.auth.dao.UserRoleMapDao;
import com.xci.platform.auth.entity.ObjectDataMapEntity;
import com.xci.platform.auth.entity.RoleEntity;
import com.xci.platform.auth.entity.UserEntity;
import com.xci.platform.core.BoolMessage;
import com.xci.platform.core.GMap;
import com.xci.platform.helper.CoreHelper;
import com.xci.platform.helper.JsonHelper;
import com.xci.platform.helper.StringHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;

/**
 * 系统角色服务
 */
@Service
public class RoleService extends AuthBaseService {

    /**
     * 角色数据访问对象
     */
    @Resource
    private RoleDao roleDao;

    /**
     * 系统用户关联角色数据访问对象
     */
    @Resource
    private UserRoleMapDao userRoleMapDao;

    /**
     * 对象资源关联数据访问接口
     */
    @Resource
    private ObjectMapDao objectMapDao;

    /**
     * 对象资源数据访问关联数据访问接口
     */
    @Resource
    private ObjectDataMapDao objectDataMapDao;

    /**
     * 保存角色
     *
     * @param entity   角色对象
     * @param isCreate 是否新建
     */
    @Validated
    @OperateUser
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage save(RoleEntity entity, Boolean isCreate) {
        //添加名称简拼
        if (StringHelper.isBlank(entity.getSpell())) {
            entity.setSpell(StringHelper.getSpell(entity.getName()));
        }
        //角色名称验证
        if (roleDao.existByName(entity.getDepartmentId(), entity.getName(), entity.getId())) {
            String msg = StringHelper.format("角色名称 {} 已经存在", entity.getName());
            return new BoolMessage(false, msg);
        }

        String msg;
        long startTime = CoreHelper.startWatch();
        if (isCreate) {
            msg = StringHelper.format("新增角色 {}", entity.getName());
            roleDao.insert(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Insert)
                    .keyValue(entity.getId())
                    .message(msg)
                    .after(entity)
            );
            //endregion
        } else {
            msg = StringHelper.format("修改角色 {}", entity.getName());
            RoleEntity oldEntity = queryById(entity.getId());
            roleDao.update(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Update)
                    .keyValue(entity.getId())
                    .message(msg)
                    .before(oldEntity)
                    .after(entity)
            );
            //endregion
        }
        // 操作日志
        operateLog(AuthConstant.SystemModule.Role, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 删除角色
     *
     * @param ids 角色主键数组
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage delete(@NotEmpty(message = "请指定删除的角色主键数组") String[] ids) {
        for (String id : ids) {
            RoleEntity oldEntity = roleDao.queryById(id);
            if (oldEntity == null) continue;
            long startTime = CoreHelper.startWatch();
            roleDao.delete(id);
            String msg = StringHelper.format("删除角色 {}", oldEntity.getName());
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Delete)
                    .keyValue(id)
                    .message(msg)
                    .before(oldEntity)
            );
            //endregion
            // 操作日志
            operateLog(AuthConstant.SystemModule.Role, msg, CoreHelper.stopWatch(startTime));
        }
        return BoolMessage.True;
    }

    // /**
    //  * 验证是否存在指定部门主键的角色
    //  *
    //  * @param departmentId 所属部门
    //  */
    // public Integer existByDeptId(String departmentId){
    //     return roleDao.existByDeptId(departmentId);
    // }

    /**
     * 更新角色启用状态
     *
     * @param id     角色主键
     * @param status 是否启用(1:启用 0:禁用)
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage updateStatus(@NotNull(message = "请指定更新状态的角色主键") String id,
                                    @NotNull(message = "请指定状态") Integer status) {
        long startTime = CoreHelper.startWatch();
        roleDao.updateStatus(id, status);
        // 操作日志
        String msg = StringHelper.format("更新角色状态 主键: {} 更新后状态: {}",
                id, status);
        operateLog(AuthConstant.SystemModule.Role, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 根据部门主键更新部门名称
     *
     * @param departmentId   部门主键
     * @param departmentName 部门名称
     */
    public BoolMessage updateDeptNameByDeptId(String departmentId, String departmentName) {
        roleDao.updateDeptNameByDeptId(departmentId, departmentName);
        return BoolMessage.True;
    }

    /**
     * 根据角色主键获取角色对象
     *
     * @param id 角色主键
     * @return 返回指定角色主键的角色对象
     */
    @Validated
    public RoleEntity queryById(@NotNull(message = "请指定角色主键") String id) {
        return roleDao.queryById(id);
    }

    /**
     * 根据用户主键查询角色列表
     *
     * @param userId 用户主键
     */
    public List<RoleEntity> queryListByUserId(@NotNull(message = "请指定用户主键") String userId) {
        return userRoleMapDao.queryRole(userId, GMap.newMap());
    }

    /**
     * 查询角色列表
     *
     * @param params 查询参数
     * @return 返回符合查询条件的角色列表
     */
    @QueryMap
    public List<RoleEntity> queryList(Map params) {
        return roleDao.query(params);
    }

    /**
     * 查询角色分页列表
     *
     * @param params 查询参数
     * @return 返回符合查询条件的角色分页列表
     */
    @QueryMap
    public Page<RoleEntity> queryPageList(Map params) {
        return (Page<RoleEntity>) roleDao.query(params);
    }

    /**
     * 生成历史记录对象
     */
    private HistoryModel.HistoryModelBuilder buildHistory() {
        return HistoryModel.builder()
                .tableName("sys_role")
                .tableCaption("系统角色")
                .keyName("id");
    }

    //region 角色关联用户

    /**
     * 添加角色关联的用户
     *
     * @param roleId  角色主键
     * @param userIds 用户主键数组
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage insertMapUser(String roleId, String[] userIds) {
        if (!ObjectUtils.isEmpty(userIds)) {
            userRoleMapDao.saveUser(roleId, userIds);
        }
        return BoolMessage.True;
    }

    /**
     * 删除角色关联的用户
     *
     * @param roleId  角色主键
     * @param userIds 用户主键数组
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage deleteMapUser(String roleId, String[] userIds) {
        if (!ObjectUtils.isEmpty(userIds)) {
            userRoleMapDao.deleteUser(roleId, userIds);
        }
        return BoolMessage.True;
    }

    /**
     * 查询角色关联的用户分页列表
     */
    @QueryMap
    public Page<UserEntity> queryMapUserPageList(String roleId, Map params) {
        return (Page<UserEntity>) userRoleMapDao.queryUser(roleId, params);
    }

    /**
     * 查询角色未关联的用户分页列表
     */
    @QueryMap
    public Page<UserEntity> queryUnMapUserPageList(String roleId, Map params) {
        return (Page<UserEntity>) userRoleMapDao.queryUnUser(roleId, params);
    }

    //endregion

    //region 角色关联模块

    /**
     * 保存角色关联的模块
     *
     * @param roleId    角色主键
     * @param moduleIds 模块主键数组
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage saveMapModule(String roleId, String[] moduleIds) {
        objectMapDao.delete(AuthConstant.Resource.Role, roleId);
        if (!ObjectUtils.isEmpty(moduleIds)) {
            objectMapDao.insert(AuthConstant.Resource.Role, roleId,
                    AuthConstant.Resource.Module, moduleIds);
        }
        return BoolMessage.True;
    }

    /**
     * 获取角色关联的模块主键列表
     *
     * @param roleId 角色主键
     */
    public List<String> queryMapModuleIdList(String roleId) {
        return objectMapDao.queryTargetIds(AuthConstant.Resource.Role,
                roleId, AuthConstant.Resource.Module);
    }

    //endregion

    //region 角色数据设置关联

    /**
     * 保存角色数据配置关联信息
     *
     * @param roleId      角色主键
     * @param dataSetting 数据配置对象
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage saveMapDataSetting(String roleId, AuthDataSetting dataSetting) {
        ObjectDataMapEntity entity = new ObjectDataMapEntity();
        entity.setObjectId(roleId);
        entity.setObjectName(AuthConstant.Resource.Role);
        entity.setDataSetting(JsonHelper.serialize(dataSetting));
        objectDataMapDao.delete(entity.getObjectName(), entity.getObjectId());
        objectDataMapDao.insert(entity);
        return BoolMessage.True;
    }

    /**
     * 查询角色数据配置关联信息
     *
     * @param roleId 角色主键
     */
    public AuthDataSetting queryMapDataSetting(String roleId) {
        AuthDataSetting dataSetting = new AuthDataSetting();
        String dataString = objectDataMapDao.queryDataSetting(AuthConstant.Resource.Role, roleId);
        if (StringHelper.isNotBlank(dataString)) {
            dataSetting = JsonHelper.deserialize(dataString, AuthDataSetting.class);
        }
        if (dataSetting.isNone()){
            dataSetting = AuthDataSetting.Self;
        }
        return dataSetting;
    }

    /**
     * 查询角色拥有的数据配置信息
     *
     * @param roleId 角色主键
     */
    public AuthDataSetting queryOwnDataSetting(String roleId) {
        return queryMapDataSetting(roleId);
    }

    //endregion
}
